package cn.arikeky.basic.service.impl;

import cn.arikeky.basic.config.BussinessException;
import cn.arikeky.basic.constant.BaseConstant;
import cn.arikeky.basic.dto.WechatLoginDto;
import cn.arikeky.basic.jwt.JwtUtils;
import cn.arikeky.basic.jwt.RsaUtils;
import cn.arikeky.basic.jwt.UserInfo;
import cn.arikeky.basic.service.ILoginService;
import cn.arikeky.basic.util.AjaxResult;
import cn.arikeky.basic.util.HttpUtil;
import cn.arikeky.basic.util.MD5Utils;
import cn.arikeky.basic.util.StrUtils;
import cn.arikeky.org.vo.LoginVo;
import cn.arikeky.system.domain.Menu;
import cn.arikeky.system.mapper.MenuMapper;
import cn.arikeky.system.mapper.PermissionMapper;
import cn.arikeky.user.domain.Logininfo;
import cn.arikeky.user.domain.User;
import cn.arikeky.user.domain.Wxuser;
import cn.arikeky.user.mapper.LogininfoMapper;
import cn.arikeky.user.mapper.UserMapper;
import cn.arikeky.user.mapper.WxuserMapper;
import com.alibaba.fastjson.JSONObject;
import com.sun.org.apache.xml.internal.security.Init;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.omg.CORBA.OBJ_ADAPTER;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.security.PrivateKey;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Service
public class LoginServiceImpl implements ILoginService {

    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private LogininfoMapper logininfoMapper;
    @Autowired
    private WxuserMapper wxuserMapper;
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private PermissionMapper permissionMapper;
    @Autowired
    private MenuMapper menuMapper;

    @Value("${jwt.rsa.pri}")
    private String privateRsaKey;

    /**
     * 帐号登录
     * @param loginVo
     * @return
     */
    @Override
    public Map<String, Object> account(LoginVo loginVo) {
        // 判断非空
        // 判断帐号是否存在
        // 判断密码是否正确
        Logininfo logininfo = logininfoMapper.loadByUsernameAndType(loginVo.getUsername(),loginVo.getType());
        if (logininfo == null){
            throw new BussinessException("账号密码错误!");
        }

        System.out.println(MD5Utils.encrypByMd5(loginVo.getPassword()));
        System.out.println(logininfo.getSalt());
        String passwordTmp = MD5Utils.encrypByMd5(loginVo.getPassword() + logininfo.getSalt());

        if (StringUtils.isBlank(passwordTmp) || !passwordTmp.equals(logininfo.getPassword())){
            throw new BussinessException("账号密码错误");
        }
        // 将token 和logininfo装到redis中
        return getMap(logininfo);
    }
    private Map<String, Object> getMap(Logininfo logininfo) {
        // 使用jwt方案
        PrivateKey privateKey = RsaUtils.getPrivateKey(JwtUtils.class.getClassLoader().getResource(privateRsaKey)
                .getFile());
        Map<String, Object> hashMap = new HashMap<String, Object>();
        UserInfo user = new UserInfo();
        user.setLogininfo(logininfo);
        if (0 == logininfo.getType()){
            // 如果是后端 那么我们需要将权限放到token中
            // 菜单权限
            List<String> permissions = permissionMapper.findByLogininfoId(logininfo.getId());
            // 按钮权限
            List<Menu> menus = menuMapper.findByLogininfoId(logininfo.getId());
            user.setPermissions(permissions);
            user.setMenus(menus);
            hashMap.put("permissions", permissions);
            hashMap.put("menus", menus);
        }
        String token = JwtUtils.generateTokenExpireInMinutes(user, privateKey, 30);
        // 将logininfo敏感字段置空
        logininfo.setPassword("");
        logininfo.setSalt("");
        logininfo.setPhone("");
        // 返回给前端
        hashMap.put("token", token);
        hashMap.put("logininfo", logininfo);
        return hashMap;
    }
//    private Map<String, Object> getMap(Logininfo logininfo) {
//        String token = UUID.randomUUID().toString();
//        redisTemplate.opsForValue().set(
//                token, logininfo,30, TimeUnit.MINUTES
//        );
//        logininfo.setPassword("");
//        logininfo.setPhone("");
//
//        Map<String,Object> map = new HashMap<String,Object>();
//        map.put("token",token);
//        map.put("logininfo", logininfo);
//        return map;
//    }

    /**
     * 微信扫码登录
     * @param dto
     * @return
     */
    @Override
    public AjaxResult wechat(WechatLoginDto dto) {
        // 验证参数
        if (StringUtils.isBlank(dto.getCode())){
            throw new BussinessException("参数不能为空");
        }
        // 根据code获取token和openid
        Pair<String, String> pair = getOpenidAndToken(dto.getCode());
        String accessToken = pair.getLeft();
        String openid = pair.getRight();
        // 根据openid查询wx_user表中的数据
        Wxuser wxuser = wxuserMapper.findWxUserByOpenId(openid);
        // 判断是否绑定/注册过
        if (null != wxuser && null != wxuser.getUserId()){
            // 已经注册过，返给前端token
            Logininfo logininfo = logininfoMapper.findByUserId(wxuser.getUserId());
            Map<String, Object> map = getMap(logininfo);
            return AjaxResult.me().success(map);
        }
        // 没注册过的用户跳转到手机号绑定页面
        StringBuffer sbr = new StringBuffer()
                .append("?accessToken=")
                .append(accessToken)
                .append("&openId=").append(openid);
        return AjaxResult.me().fail("nobinder").setResultObj(sbr);
    }

    /**
     * 微信扫码登录绑定
     * @param dto
     */
    @Override
    public Map<String, Object> wechatBbinder(WechatLoginDto dto) {
        // 校验参数非空
        // 校验验证码是否正确
        this.verifySmsCode(dto);
        // 调用微信接口获取用户信息
        String url = String.format(BaseConstant.WxLoginConstant.WX_LOGIN_USER_INFO,
                dto.getAccessToken(), dto.getOpenId());
        String str = HttpUtil.httpGet(url);
        // JOSN 字符串转成我们的实体
        Wxuser wxuser = JSONObject.parseObject(str, Wxuser.class);
//        System.out.println(wxuser);
        // 存储用户信息
        // 三张数据表
        User user = userMapper.findPhoneById(dto.getPhone());
        Logininfo logininfo = new Logininfo();
        if (null == user){
            // 初始化user对象
            user = initUser(dto);
            // 初始化logininfo对象
            logininfo = initLogininfo(user);
            // 保存logininfo
            logininfoMapper.add(logininfo);
            // 保存user
            user.setLogininfoId(logininfo.getId());
            userMapper.add(user);
        } else {
            // 获取logininfo对象
            logininfo = logininfoMapper.findByUserId(user.getId());
        }
        wxuser.setUserId(user.getId());
        wxuserMapper.add(wxuser);
        // 保存wxuser
        return getMap(logininfo);
    }

    private Logininfo initLogininfo(User user){
        Logininfo logininfo = new Logininfo();
        BeanUtils.copyProperties(user, logininfo);
        return logininfo;
    }

    private User initUser(WechatLoginDto dto){
        // 盐值
        String salt = StrUtils.getComplexRandomString(32);
        // 密码
        String password = salt + "1";
        String encryPwd = MD5Utils.encrypByMd5(password);
        User user = new User();
        user.setUsername(dto.getPhone());
        user.setPhone(dto.getPhone());
        user.setSalt(salt);
        user.setPassword(encryPwd);
        user.setState(1);
        user.setCreatetime(new Date());
        return user;

    }



    private void verifySmsCode(WechatLoginDto dto) {
        String redisSmsCode = (String) redisTemplate.opsForValue().get(String.format(
                BaseConstant.VerifyCodeConstant.BINDER_REGISTER_PREFIX, dto.getPhone()));
        if (StringUtils.isBlank(redisSmsCode)){
            throw new BussinessException("验证码错误！");
        }
        String smsCode = redisSmsCode.split(":")[0];
        if (!smsCode.equals(dto.getVerifyCode())){
            throw new BussinessException("验证码错误！");
        }
    }

    // 通过code 获取token和openid
    private Pair<String, String> getOpenidAndToken(String code){
        String tokenUrl = String.format(BaseConstant.WxLoginConstant.WX_LOGIN_TOKEN_URL,
                BaseConstant.WxLoginConstant.WX_LOGIN_APPID,
                BaseConstant.WxLoginConstant.WX_LOGIN_SECRET,
                code);
        String str = HttpUtil.httpGet(tokenUrl);
//        System.out.println(str);
        JSONObject jsonObject = JSONObject.parseObject(str);
        String accessToken = jsonObject.getString("access_token");
        String openid = jsonObject.getString("openid");
        return Pair.of(accessToken,openid);
    }
}
